package com.yetianhua.sqlsession;

import com.yetianhua.executor.Executor;
import com.yetianhua.executor.SimpleExecutor;
import com.yetianhua.pojo.Configuration;
import com.yetianhua.pojo.MappedStatement;

import java.lang.reflect.*;
import java.util.List;

/**
 * @author yetianhua
 * @className DefaultSqlSession
 * @description TODO
 * @date 2021/6/2
 * @time 20:24
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    private Executor executor = new SimpleExecutor();

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    public <T> List<T> selectList(String statementId, Object... param) throws Exception {
        List<T> result = executor.query(configuration, configuration.getMappedStatementMap().get(statementId), param);
        return result;
    }

    public <T> T selectOne(String statementId, Object... param) throws Exception {
        return (T) selectList(statementId, param).get(0);
    }

    public int updateById(String statementId, Object... param) throws Exception{
        return executor.updateOrDelete(configuration, configuration.getMappedStatementMap().get(statementId), param);
    }

    public int deleteById(String statementId, Object... param) throws Exception{
        return executor.updateOrDelete(configuration, configuration.getMappedStatementMap().get(statementId), param);
    }

    public <T> T getMapper(Class<T> mapperClass) {
        Object obj = Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String statementId = className + "." + methodName;

                Class<?> returnType = method.getReturnType();
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                
                switch(mappedStatement.getSqlCommandType()){
                    case SELECT:{
                        // 准备参数2：params:args
                        // 获取被调用方法的返回值类型
                        Type genericReturnType = method.getGenericReturnType();
                        // 判断是否进行了 泛型类型参数化
                        if (genericReturnType instanceof ParameterizedType) {
                            List<Object> objects = selectList(statementId, args);
                            return objects;
                        } else {
                            return selectOne(statementId, args);
                        }
                    }
                    case UPDATE: {
                        int updateRows = updateById(statementId, args);
                        return handlerUpdateOrDeleteReturn(returnType, updateRows);
                    }
                    case DELETE: {
                        int deleteRow = deleteById(statementId, args);
                        return handlerUpdateOrDeleteReturn(returnType, deleteRow);
                    }
                }
                return null;

            }
        });
        return (T) obj;
    }

    private Object handlerUpdateOrDeleteReturn(Class<?> returnType, int rows) {
        if(boolean.class.equals(returnType) || Boolean.class.equals(returnType)){
            return rows > 0;
        } else if(int.class.equals(returnType) || Integer.class.equals(returnType)){
            return rows;
        } else if(long.class.equals(returnType) || Long.class.equals(returnType)){
            return Long.valueOf(rows);
        } else if(void.class.equals(returnType)){
            return null;
        } else {
            throw new RuntimeException("返回结果类型有问题");
        }
    }
}
